/*
 * Copyright 2013 Krzysztof Smigielski
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.nilcaream.dirstore.core.common;

import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.common.io.InputSupplier;
import com.nilcaream.dirstore.core.model.TrimmedElement;

import java.io.*;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * TODO
 * <p/>
 * Krzysztof Smigielski 4/21/13 2:45 PM
 */
public class Utils {

    private static final String[] UNITS = new String[]{"B", "KB", "MB", "GB", "TB"};

    private static final Cache<String, List<ZipEntry>> ZIP_ENTRIES_CACHE = CacheBuilder.newBuilder().recordStats().build();

    /**
     * http://stackoverflow.com/a/5599842
     */
    public static String toReadableFileSize(long size) {
        String result;
        if (size <= 0) {
            result = "0";
        } else {
            int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
            result = new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + UNITS[digitGroups];
        }
        return result;
    }

    public static <T> Iterable<T> toIterable(final Enumeration<T> enumeration) {
        return new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return new Iterator<T>() {
                    @Override
                    public boolean hasNext() {
                        return enumeration.hasMoreElements();
                    }

                    @Override
                    public T next() {
                        return enumeration.nextElement();
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException("cannot remove items from enumeration");
                    }
                };
            }
        };
    }

    public static TrimmedElement createEmpty() {
        TrimmedElement element = new TrimmedElement();
        element.setCompressionNeeded(false);
        element.setData(new byte[]{});
        return element;
    }

    public static long calculateCrc32(final InputStream inputStream) throws IOException {
        return ByteStreams.hash(new InputSupplier<InputStream>() {
            @Override
            public InputStream getInput() throws IOException {
                return inputStream;
            }
        }, Hashing.crc32()).padToLong();
    }

    public static long calculateCrc32(File file) throws IOException {
        return Files.hash(file, Hashing.crc32()).padToLong();
    }

    public InputStream clone(InputStream inputStream) throws IOException {
        return new ByteArrayInputStream(ByteStreams.toByteArray(inputStream));
    }

    public static File[] findFiles(String glob, String directory) {
        final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + glob);
        return Paths.get(directory).toFile().listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return matcher.matches(Paths.get(file.toURI()));
            }
        });
    }

    public static List<ZipEntry> getEntries(final ZipFile zipFile) {
        try {
            return ZIP_ENTRIES_CACHE.get(zipFile.getName() + zipFile.size(), new Callable<List<ZipEntry>>() {
                @Override
                public List<ZipEntry> call() throws Exception {
                    return ImmutableList.copyOf(toIterable(zipFile.entries()));
                }
            });
        } catch (ExecutionException e) {
            throw Throwables.propagate(e);
        }
    }

    public static String getCacheStats() {
        return ZIP_ENTRIES_CACHE.stats().toString();
    }

    public static void clearCache() {
        ZIP_ENTRIES_CACHE.invalidateAll();
    }
}
